Skip to content

fix: resolve i18n issues with locale detection and hardcoded strings#58

Merged
KoheiYamashita merged 7 commits intomainfrom
fix/i18n-issues-54
Mar 10, 2026
Merged

fix: resolve i18n issues with locale detection and hardcoded strings#58
KoheiYamashita merged 7 commits intomainfrom
fix/i18n-issues-54

Conversation

@KoheiYamashita
Copy link
Copy Markdown
Member

📝 Description

Fix multiple i18n issues where Android per-app language settings were not reaching the Go backend, and numerous user-facing strings were hardcoded instead of using string resources / i18n message catalogs.

🗣️ Type of Change

  • 🐞 Bug fix (non-breaking change which fixes an issue)

🔗 Linked Issue

Closes #54

📚 Technical Context (Skip for Docs)

  • Reasoning: Android's per-app locale (Settings > Apps > Language) uses context.resources.configuration.locales[0], but the code was using Locale.getDefault() which returns the system-wide locale. This caused WebSocket and Config API requests to send the wrong language to the Go backend.

Changes

Phase 1 — Android locale fix:

  • WebSocketClient.kt / ConfigApiClient.kt: inject Context, use context.resources.configuration.locales[0].language instead of Locale.getDefault().language
  • Update Koin modules (AppModule.kt, ConfigModule.kt) to pass androidContext()

Phase 2 — Android hardcoded string extraction:

  • app module: AssistantService accessibility dialog, notification text, CalendarPickerActivity
  • feature/chat module: VoiceModeOverlay phase labels, MessageInput content descriptions, ChatScreen title/settings
  • backend/config module (new res/values/strings.xml): ConfigSectionDetailScreen, ConfigSectionListScreen
  • All strings added for both en and ja

Phase 3 — Go backend i18n:

  • New pkg/i18n/messages_channel.go with en/ja translations for Telegram, WebSocket, and agent loop command messages
  • telegram.go: propagate user locale via localeFromMessage() helper, add to metadata
  • telegram_commands.go: all command responses use i18n.T()/i18n.Tf()
  • websocket.go: "Configuration required" → i18n
  • loop.go: all /show, /list, /switch command responses → i18n

🧪 Test Environment & Hardware

  • Go tests: make check passes (fmt/vet/test)
  • Android: Build not verified (no Java environment available)

☑️ Checklist

  • My code/docs follow the style of this project.
  • I have performed a self-review of my own changes.
  • I have updated the documentation accordingly.

🤖 Generated with Claude Code

…54)

- Fix Android per-app locale not reaching Go backend by using
  Context.resources.configuration instead of Locale.getDefault()
- Extract all user-facing hardcoded strings in Android UI to string
  resources (app, feature/chat, backend/config modules)
- Add Go i18n messages for Telegram, WebSocket, and agent loop commands
- Propagate locale from Telegram user language and WebSocket metadata

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the internationalization (i18n) capabilities of the application across both its Android frontend and Go backend. It addresses critical issues such as incorrect locale detection on Android, ensuring that per-app language settings are respected. Furthermore, it systematically replaces hardcoded user-facing strings with localized string resources in the Android application and integrates a robust i18n system into the Go backend for command responses and channel messages, providing a more consistent and localized user experience.

Highlights

  • Android Locale Detection Fix: Corrected Android's locale detection mechanism to use per-app language settings (context.resources.configuration.locales[0].language) instead of the system-wide default (Locale.getDefault()) for WebSocket and Config API requests, ensuring proper localization.
  • Hardcoded String Extraction (Android): Extracted numerous hardcoded user-facing strings into localized string resources across various Android UI components, including AssistantService, CalendarPickerActivity, VoiceModeOverlay, MessageInput, ChatScreen, ConfigSectionDetailScreen, and ConfigSectionListScreen.
  • New Android String Resources: Introduced new strings.xml files for English and Japanese in the app, backend/config, and feature/chat modules to support the newly extracted strings and facilitate future internationalization efforts.
  • Go Backend Internationalization: Implemented i18n for Telegram, WebSocket, and agent loop command messages by introducing a new pkg/i18n/messages_channel.go file with English and Japanese translations, and integrating i18n.T()/i18n.Tf() functions for all user-facing command responses.
  • Locale Propagation (Go): Enhanced the Telegram channel to propagate the user's locale via message metadata to the Go backend, allowing for localized responses based on the user's preferred language.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • android/app/src/main/java/io/clawdroid/CalendarPickerActivity.kt
    • Replaced hardcoded "Select Calendar" and "Cancel" strings with stringResource calls.
  • android/app/src/main/java/io/clawdroid/assistant/AssistantService.kt
    • Replaced hardcoded Japanese strings for accessibility guide title, body, cancel, open settings, and notification listening text with getString(R.string...) calls.
  • android/app/src/main/java/io/clawdroid/di/AppModule.kt
    • Modified WebSocketClient instantiation to inject androidContext() for proper locale detection.
  • android/app/src/main/res/values-ja/strings.xml
    • Added new Japanese string resources for common actions, accessibility guide, assistant notification, and calendar picker.
  • android/app/src/main/res/values/strings.xml
    • Added new English string resources for common actions, accessibility guide, assistant notification, and calendar picker.
  • android/backend/config/src/main/java/io/clawdroid/backend/config/ConfigApiClient.kt
    • Imported android.content.Context.
    • Modified constructor to accept Context.
    • Updated Accept-Language header to use context.resources.configuration.locales[0].language instead of Locale.getDefault().language.
  • android/backend/config/src/main/java/io/clawdroid/backend/config/ConfigModule.kt
    • Updated ConfigApiClient dependency injection to provide androidContext().
  • android/backend/config/src/main/java/io/clawdroid/backend/config/ConfigSectionDetailScreen.kt
    • Imported stringResource.
    • Replaced various hardcoded strings (e.g., "Saved.", "Error:", "Back", "Save", "Show", "Hide", "Comma-separated values", "Internal storage only", "Browse", "Pick each time", "Select Calendar", "Cancel") with stringResource or context.getString calls.
  • android/backend/config/src/main/java/io/clawdroid/backend/config/ConfigSectionListScreen.kt
    • Imported stringResource.
    • Replaced hardcoded strings (e.g., "Backend Config", "Back", "Retry", "Connection Settings", "Open", "fields") with stringResource calls.
  • android/backend/config/src/main/res/values-ja/strings.xml
    • Added new Japanese string resources for config screens.
  • android/backend/config/src/main/res/values/strings.xml
    • Added new English string resources for config screens.
  • android/core/data/src/main/java/io/clawdroid/core/data/remote/WebSocketClient.kt
    • Imported android.content.Context.
    • Modified constructor to accept Context.
    • Updated WebSocket URL construction to use context.resources.configuration.locales[0].language for the locale parameter.
  • android/feature/chat/src/main/java/io/clawdroid/feature/chat/component/MessageInput.kt
    • Imported stringResource and io.clawdroid.feature.chat.R.
    • Replaced hardcoded content descriptions and placeholder text (e.g., "Camera", "Gallery", "Voice", "Message...", "Send") with stringResource calls.
  • android/feature/chat/src/main/java/io/clawdroid/feature/chat/screen/ChatScreen.kt
    • Imported stringResource and io.clawdroid.feature.chat.R.
    • Replaced hardcoded title "ClawDroid" and content description "Settings" with stringResource calls.
  • android/feature/chat/src/main/java/io/clawdroid/feature/chat/voice/VoiceModeOverlay.kt
    • Imported stringResource and io.clawdroid.feature.chat.R.
    • Replaced hardcoded content descriptions and voice phase labels (e.g., "Close", "Turn off camera", "Listening...", "Paused", "Sending...", "Thinking...", "Speaking...", "Error") with stringResource calls.
    • Added @Composable annotation to phaseLabel function.
  • android/feature/chat/src/main/res/values-ja/strings.xml
    • Added new Japanese string resources for voice mode, message input, and chat screen.
  • android/feature/chat/src/main/res/values/strings.xml
    • Added new English string resources for voice mode, message input, and chat screen.
  • pkg/agent/loop.go
    • Removed initial handleCommand call before locale extraction.
    • Modified handleCommand to accept a locale parameter.
    • Updated command responses (/show, /list, /switch) to use i18n.T and i18n.Tf for localization.
    • Changed locale extraction to use the provided l directly without i18n.NormalizeLocale.
  • pkg/channels/telegram.go
    • Imported github.com/KarakuriAgent/clawdroid/pkg/i18n.
    • Added localeFromMessage helper function to extract locale from Telegram message.
    • Used i18n.T for the "Thinking..." message.
    • Added locale to message metadata.
  • pkg/channels/telegram_commands.go
    • Imported github.com/KarakuriAgent/clawdroid/pkg/i18n.
    • Replaced all hardcoded command responses (/start, /help, /show, /list) with i18n.T and i18n.Tf calls.
    • Added localeFromMessage helper function.
  • pkg/channels/websocket.go
    • Imported github.com/KarakuriAgent/clawdroid/pkg/i18n.
    • Used i18n.T for the "Configuration required" message.
  • pkg/i18n/messages_channel.go
    • Added a new Go file defining English and Japanese translations for Telegram, WebSocket, and agent loop command messages.
Activity
  • No human activity recorded for this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses internationalization issues by replacing hardcoded strings with localized resources across both the Android application and the Go backend. The Android-side changes correctly implement per-app locale detection for API requests. The Go backend is updated to handle locales and serve translated responses for commands. My review identifies a few areas for improvement in the Go backend, mainly concerning inconsistencies in command behavior and translations between different interfaces (agent vs. Telegram). Addressing these will enhance the consistency and maintainability of the command handling logic.

Comment on lines 1397 to +1398
case "models":
// TODO: Fetch available models dynamically if possible
return "Available models: glm-4.7, claude-3-5-sonnet, gpt-4o (configured in config.json/env)", true
return i18n.T(locale, "agent.cmd.list.models"), true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The /list models command here returns a hardcoded list of example models, which is inconsistent with the same command in the Telegram channel that shows the currently configured model. The latter behavior is more useful and should be implemented here for consistency. This also removes the need for the agent.cmd.list.models translation key.

Suggested change
case "models":
// TODO: Fetch available models dynamically if possible
return "Available models: glm-4.7, claude-3-5-sonnet, gpt-4o (configured in config.json/env)", true
return i18n.T(locale, "agent.cmd.list.models"), true
case "models":
return i18n.Tf(locale, "cmd.list.models", al.model), true

Comment on lines +19 to +31
"cmd.show.model": "Current Model: %s",
"cmd.show.channel": "Current Channel: telegram",
"cmd.show.unknown": "Unknown parameter: %s. Try 'model' or 'channel'.",
"cmd.list.usage": "Usage: /list [models|channels]",
"cmd.list.models": "Configured Model: %s\n\nTo change models, update config.json",
"cmd.list.channels": "Enabled Channels:\n- %s",
"cmd.list.unknown": "Unknown parameter: %s. Try 'models' or 'channels'.",

// Agent loop commands (/show, /list, /switch)
// cmd.show.usage and cmd.list.usage are shared with Telegram commands
"agent.cmd.show.model": "Current model: %s",
"agent.cmd.show.channel": "Current channel: %s",
"agent.cmd.show.unknown": "Unknown show target: %s",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There are duplicated and inconsistent translation keys for the /show command between the agent and Telegram command handlers. For example, cmd.show.model and agent.cmd.show.model differ only in capitalization, and cmd.show.channel is hardcoded while agent.cmd.show.channel uses a format specifier.

To improve consistency and reduce duplication, these should be unified into a single set of keys (e.g., under the cmd. prefix) and used across both handlers.

For example:

  • Unify on "cmd.show.model": "Current model: %s".
  • Change "cmd.show.channel" to "Current channel: %s" and use i18n.Tf(locale, "cmd.show.channel", "telegram") in telegram_commands.go.

This would make the translations more reusable and consistent.

Comment on lines +24 to +34
"cmd.list.channels": "Enabled Channels:\n- %s",
"cmd.list.unknown": "Unknown parameter: %s. Try 'models' or 'channels'.",

// Agent loop commands (/show, /list, /switch)
// cmd.show.usage and cmd.list.usage are shared with Telegram commands
"agent.cmd.show.model": "Current model: %s",
"agent.cmd.show.channel": "Current channel: %s",
"agent.cmd.show.unknown": "Unknown show target: %s",
"agent.cmd.list.models": "Available models: glm-4.7, claude-3-5-sonnet, gpt-4o (configured in config.json/env)",
"agent.cmd.list.no_channels": "No channels enabled",
"agent.cmd.list.channels": "Enabled channels: %s",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The translation keys for the /list channels command imply different output formats. cmd.list.channels is designed for a newline-separated list (which is good for chat), while agent.cmd.list.channels is for a simple string, used with comma-separation in agent/loop.go. The capitalization also differs.

For better consistency, consider unifying these. For example, you could have a single key like "cmd.list.channels": "Enabled channels: %s" and apply the specific formatting (like \n- or , ) at the call site.

KoheiYamashita and others added 6 commits March 10, 2026 16:23
WebSocket clients may send non-normalized locale values (e.g. "ja-JP",
"JA") which need to be normalized before use in i18n lookups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AssistantConnectionImpl directly instantiates WebSocketClient which now
requires a Context parameter for per-app locale detection. Pass Context
through from AssistantService to fix the compilation error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WebSocketClient only needs Context for locale detection via
resources.configuration, so applicationContext is sufficient and
prevents holding a reference to the Service after onDestroy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ffect

Use stringResource() in Composable scope and pass pre-resolved strings
into LaunchedEffect. Replace config_error format string with
config_error_prefix for simple concatenation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pre-resolve string resource in Composable scope before using it
inside coroutine launched from ActivityResult callback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…elp tabs

- Move locale extraction before rate limit check so error messages can be localized
- Add agent.rate_limited / agent.rate_limited_tool i18n keys (en/ja)
- Extract hardcoded "Select" contentDescription to string resource (config_select)
- Remove trailing tab characters from cmd.help values in en/ja

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member Author

@KoheiYamashita KoheiYamashita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コードレビュー

全体としてしっかりした PR です。ロケール修正のアプローチは正しく、Koin の配線も適切で、en/ja の翻訳も完全です。


良い点

  • context.resources.configuration.locales[0].language は Android 13+ のアプリごとの言語設定を正しく反映する正しいアプローチ
  • androidContext() が適切に渡されており、AssistantService では applicationContext を使って Service コンテキストのリークを防止
  • 全ての新規キーが en/ja 両方に存在し、翻訳も適切
  • Go の i18n が messages_status.go / messages_config.go / messages_agent.go の既存パターンに準拠

修正すべき問題

1. (中) 通知タイトルがハードコードのまま

AssistantService.ktsetContentText は文字列リソースに抽出されたのに、同じ行の setContentTitle("ClawDroid Assistant") がハードコードのまま。ブランド名であっても、この PR の趣旨に合わせて文字列リソースに抽出すべき。

2. (中) WebSocketClient のパラメータ順序

WebSocketClient.ktcontext(必須・デフォルトなし)が clientType(省略可能・デフォルト "main")の後に配置されている。Kotlin の慣例では必須パラメータを省略可能パラメータの前に置くべき。

3. (中) cmd.show.channel が "telegram" をハードコード

messages_channel.go:20"cmd.show.channel": "Current Channel: telegram" とハードコードされているが、agent loop 版の agent.cmd.show.channel は正しく %s フォーマットを使用。一貫性のためにパラメータ化すべき。

4. (低) Cancel 文字列キーがモジュール間で重複

config_cancel(backend/config)と action_cancel(app)が同一内容。共通文字列は core/ui モジュールへの移動を検討。

5. (低) 共有 i18n キーの暗黙的な結合

agent loop コマンドが Telegram 名前空間の cmd.show.usage / cmd.list.usage を再利用。コメントで文書化されてはいるが、将来的に分岐が必要になった場合に問題となる可能性あり。


総評

1〜3 はマージ前に修正推奨、4〜5 は任意の改善事項です。

@KoheiYamashita KoheiYamashita merged commit ab6ecea into main Mar 10, 2026
16 checks passed
@KoheiYamashita KoheiYamashita deleted the fix/i18n-issues-54 branch March 10, 2026 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] ステータスラベルの多言語化が正しく動作しない(locale 取得ミス+ハードコード)

1 participant